#ifndef _OPENGL_SHADER_H_
#define _OPENGL_SHADER_H_
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static GLuint
shader_load_str(GLenum shadertype, const GLchar *shadercode) {
	GLuint shader = glCreateShader(shadertype);
	glShaderSource(shader, 1, &shadercode, NULL);
	glCompileShader(shader);

	GLint result, loglen;
  	glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
  	glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &loglen);
  	printf("compile shader status, result:%d loglen:%d\n", result, loglen);
  	if (loglen > 1) {
  		GLchar *log = (GLchar*)malloc(loglen + 1);
  		memset(log, 0, loglen + 1);
  		glGetShaderInfoLog(shader, loglen, NULL, log);
  		printf("compile shader log:[%s]\n", log);
  		free(log);
  	}

	if (result == GL_FALSE) {
  		glDeleteShader(shader);
  		shader = 0;
  		printf("fail to compile shader\n");
  	}
	return shader;
}

static GLuint
shader_linkprogram_str(const GLchar *vertexcode, const GLchar *fragmentcode) {
	GLuint vertexshader = shader_load_str(GL_VERTEX_SHADER, vertexcode);
	if (vertexshader == 0) {
		printf("fail to load vertex shader str\n");
		return 0;
	}
	GLuint fragmentshader  = shader_load_str(GL_FRAGMENT_SHADER, fragmentcode);
	if (fragmentshader == 0) {
		glDeleteShader(vertexshader);
		printf("fail to load fragment shader str\n");
		return 0;
	}

	GLuint program = glCreateProgram();
	glAttachShader(program, vertexshader);
	glAttachShader(program, fragmentshader);
	glLinkProgram(program);

	GLint result, loglen;
	glGetProgramiv(program, GL_LINK_STATUS, &result);
	glGetProgramiv(program, GL_INFO_LOG_LENGTH, &loglen);
	printf("link program status, result:%d, loglen:%d\n", result, loglen);
	if (loglen > 1) {
		GLchar *log = (GLchar*)malloc(loglen + 1);
		memset(log, 0, loglen + 1);
		glGetProgramInfoLog(program, loglen, NULL, log);
		free(log);
		printf("link program log:[%s]\n", log);
	}

	glDetachShader(program, vertexshader);
	glDetachShader(program, fragmentshader);
	glDeleteShader(vertexshader);
	glDeleteShader(fragmentshader);

	if (result == GL_TRUE) {
		printf("link program successfully, delete no use shader %d %d\n", vertexshader, fragmentshader);
	} else {
		glDeleteProgram(program);
		program = 0;
	}
	return program;
}

GLuint 
shader_load_file(const char *filename, GLenum shadertype) {
	FILE *file = fopen(filename, "rb");
	if (file == NULL) {
		printf("fail to open file:%s\n", filename);
		return 0;
	}

	fpos_t beginpos;
	fgetpos(file, &beginpos);

	fseek(file, 0, SEEK_END);
  	long size = ftell(file); // get file size
  	fsetpos(file, &beginpos);

  	int len = (int)(size + 1); // just plus '\0' for a simple way
  	char *content = (char*)malloc(len);
  	memset(content, 0, len);
  	fread(content, len, 1, file); // never use fgets here
  	fclose(file);

  	// printf("content:[\n%s]\n", content); // just debug
  	GLuint shader = glCreateShader(shadertype);
  	const char *ptr = (const char *)content;
  	glShaderSource(shader, 1, &ptr, NULL);
  	glCompileShader(shader);
  	free(content);

  	GLint result, loglen;
  	glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
  	glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &loglen);
  	printf("compile shader status, result:%d loglen:%d\n", result, loglen);
  	if (loglen > 1) {
  		GLchar *log = (GLchar*)malloc(loglen + 1);
  		memset(log, 0, loglen + 1);
  		glGetShaderInfoLog(shader, loglen, NULL, log);
  		printf("compile shader log:[%s]\n", log);
  		free(log);
  	}

  	if (result == GL_FALSE) {
  		glDeleteShader(shader);
  		shader = 0;
  		printf("fail to compile shader\n");
  	}
	return shader;
}

GLuint 
shader_linkprogram_file(const char *vertexfile, const char* fragmentfile) {
	GLuint vertexshader = shader_load_file(vertexfile, GL_VERTEX_SHADER);
	if (vertexshader == 0) {
		printf("fail to load vertex shader file\n");
		return 0;
	}
	GLuint fragmentshader = shader_load_file(fragmentfile, GL_FRAGMENT_SHADER);
	if (fragmentshader == 0) {
		printf("fail to load fragment shader file\n");
		return 0;
	}

	GLuint program = 0;
	program = glCreateProgram();
	glAttachShader(program, vertexshader);
	glAttachShader(program, fragmentshader);
	glLinkProgram(program);

	GLint result, loglen;
	glGetProgramiv(program, GL_LINK_STATUS, &result);
	glGetProgramiv(program, GL_INFO_LOG_LENGTH, &loglen);
	printf("link program status, result:%d, loglen:%d\n", result, loglen);
	if (loglen > 1) {
		GLchar *log = (GLchar*)malloc(loglen + 1);
		memset(log, 0, loglen + 1);
		glGetProgramInfoLog(program, loglen, NULL, log);
		printf("link program log:[%s]\n", log);
		free(log);
	}

	glDetachShader(program, vertexshader);
	glDetachShader(program, fragmentshader);
	glDeleteShader(vertexshader);
	glDeleteShader(fragmentshader);

	if (result == GL_TRUE) {
		printf("link program successfully, delete no use shader %d %d\n", vertexshader, fragmentshader);
	} else {
		glDeleteProgram(program);
		program = 0;
	}
	return program;
}

#endif
